/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.enrstu.service.impl;

import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.enrstu.domain.FileInformation;
import me.zhengjie.modules.enrstu.repository.FileInformationRepository;
import me.zhengjie.modules.enrstu.service.EnrollmentParameterService;
import me.zhengjie.modules.enrstu.service.FileInformationService;
import me.zhengjie.modules.enrstu.service.GuardianChildrenService;
import me.zhengjie.modules.enrstu.service.dto.*;
import me.zhengjie.modules.enrstu.service.mapstruct.FileInformationMapper;
import me.zhengjie.utils.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static cn.hutool.core.util.IdUtil.simpleUUID;

/**
* @website https://eladmin.vip
* @description 服务实现
* @author yyx
* @date 2024-11-01
**/
@Slf4j
@Service
@RequiredArgsConstructor
public class FileInformationServiceImpl implements FileInformationService {

    private final FileInformationRepository fileInformationRepository;
    private final FileInformationMapper fileInformationMapper;
    private final GuardianChildrenService gcService;
    private final EnrollmentParameterService epService;
    private final String guardian = "info_guardian_information";
    private final String children = "info_children_information";
    @Value("${spring.file.upload-dir}")
    private String uploadDir;
    private String tmp = "";




    @Override
    public void fileUploadUpdate(List<MultipartFile> files, SignUpFileInfoCURDHeaderDto fileInfoCreateOrUpdate ){
        if (ObjectUtil.isNull(fileInfoCreateOrUpdate.getLinkId())){
            throw new RuntimeException("关联表ID不能为空");
        }

        files.forEach(file -> {
                try {
                    if (file.isEmpty()) {
                        return;
                    }
                    // 附件信息表存储对应实体类
                    FileInformation fileInformation = new FileInformation();
                    //附件类型名称
                    fileInformation.setFileAttachmentCode(fileInfoCreateOrUpdate.getFileAttachmentCode());
                    // 文件名
                    fileInformation.setFileName(FileUtil.getFileNameNoEx(file.getOriginalFilename()));
                    //文件大小
                    fileInformation.setFileSize(FileUtil.getSize(file.getSize()));
                    //文件类型
                    fileInformation.setFileType(FileUtil.getFileType(file.getContentType()));
                    //关联表的主键ID
                    if (fileInfoCreateOrUpdate.getIsChildrenFile()) {
                        // 上传的是儿童信息,则关联儿童表
                        fileInformation.setLinkName(children);
                    } else {
                        // 上传的是监护人信息,则关联监护人表
                        fileInformation.setLinkName(guardian);
                    }
                    //关联表的主键ID
                    fileInformation.setLinkId(fileInfoCreateOrUpdate.getLinkId());
                    fileInformation.setGcId(fileInfoCreateOrUpdate.getGcId());
                    // 主键ID
                    fileInformation.setId(simpleUUID());
                    String uniqueFileName = "/" + fileInformation.getLinkName() + "/" + fileInformation.getLinkId() + "/" + fileInformation.getId() +
                            file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
                    fileInformation.setFilePath(uniqueFileName);
                    Path filePath = Paths.get(this.uploadDir, uniqueFileName);
                    // 确保文件路径存在
                    File parentDir = filePath.getParent().toFile();
                    if (!parentDir.exists()) {
                        parentDir.mkdirs();
                    }
                    // 写入文件
                    Files.write(filePath, file.getBytes());
                    fileInformationRepository.save(fileInformation);

                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
        });
    }
    @Override
    public void fileUploadCreate(List<MultipartFile> files,String gcId,String likeId,Boolean isChildrenFile) {
        log.info("files的数量：{}",files.size());
        files.forEach(file -> {
            try {
                if (file.isEmpty()) {
                    return;
                }
                String originalFilename = "";
                // 附件信息表存储对应实体类
                FileInformation fileInformation = new FileInformation();
                //附件类型名称
                String fileName = file.getOriginalFilename();
                if (ObjectUtil.isNotNull(fileName)){
                    int firstUnderscoreIndex = fileName.indexOf('_', fileName.indexOf("_") + 1);
                    //处理前端传输文件名，提取出附件类型参数名
                    fileInformation.setFileAttachmentCode(fileName.substring(0, firstUnderscoreIndex));
                }

                //关联表的主键ID
                if (isChildrenFile) {
                    // 上传的是儿童信息,则关联儿童表
                    fileInformation.setLinkName(children);
                } else {
                    // 上传的是监护人信息,则关联监护人表
                    fileInformation.setLinkName(guardian);
                }
                //关联表的主键ID
                fileInformation.setLinkId(likeId);
                fileInformation.setGcId(gcId);
                // 主键ID
                fileInformation.setId(simpleUUID());
                // 文件名
                fileInformation.setFileName(FileUtil.getFileNameNoEx(file.getOriginalFilename()));
                //文件大小
                fileInformation.setFileSize(FileUtil.getSize(file.getSize()));

                //文件类型
                fileInformation.setFileType(FileUtil.getFileType(file.getContentType()));

                String uniqueFileName = "/" + fileInformation.getLinkName() + "/" + fileInformation.getLinkId() + "/" + fileInformation.getId() +
                        file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
                fileInformation.setFilePath(uniqueFileName);
                log.info("文件名：{}", uniqueFileName);
                Path filePath = Paths.get(this.uploadDir, uniqueFileName);
                log.info("文件路径：{}", filePath);
                // 确保文件路径存在
                File parentDir = filePath.getParent().toFile();
                if (!parentDir.exists()) {
                    parentDir.mkdirs();
                }
                // 写入文件
                Files.write(filePath, file.getBytes());
                log.info("文件 {} 已保存到 {}", uniqueFileName, filePath.toAbsolutePath());
                 fileInformationRepository.save(fileInformation);


            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    @Override
    public List<FileInformation> fileInfo(List<String> linkIds) {
        List<FileInformation> fileList = new ArrayList<>();
        linkIds.forEach(linkId -> {
            fileList.addAll(fileInformationRepository.findByLinkId(linkId));
        });
        return fileList;
    }


    @Override
    public byte[] fileDownload(List<String> linkIds) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(bos)) {
            linkIds.forEach(linkId -> {
                List<FileInformation> fileInformationList = fileInformationRepository.findByLinkId(linkId);
                log.info("fileInformationList for linkId {}: {}", linkId, fileInformationList);
                fileInformationList.forEach(fileInformation -> {
                    try {
                        Path path = Paths.get(this.uploadDir, fileInformation.getFilePath());
                        String fileName = fileInformation.getFileName() + fileInformation.getFilePath().substring(fileInformation.getFilePath().lastIndexOf("."));

                        ZipEntry zipEntry = new ZipEntry(fileName);
                        zos.putNextEntry(zipEntry);
                        Files.copy(path, zos);
                        zos.closeEntry();
                    } catch (IOException e) {
                        log.error("Error processing file for linkId: {}", linkId, e);
                        throw new RuntimeException(e);
                    }
                });
            });
            zos.finish();
            zos.flush();
        } catch (IOException e) {
            log.error("Error processing linkIds: {}", linkIds, e);
            throw new RuntimeException(e);
        }
        return bos.toByteArray();
    }

    @Override
    public  byte[] fileDownload(String LinkId,String fileAttachmentCode) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try (
             ZipOutputStream zos = new ZipOutputStream(bos)){
            List<FileInformation> fileInformationList = fileInformationRepository.findByLinkIdAndFileAttachmentCode(LinkId,fileAttachmentCode);
            log.info("fileInformationList:{}",fileInformationList);
            fileInformationList.forEach(fileInformation -> {
                try {
                    Path path = Paths.get(this.uploadDir, fileInformation.getFilePath());
                    String fileName = fileInformation.getFileName() + fileInformation.getFilePath().substring(fileInformation.getFilePath().lastIndexOf("."));
                    log.info("fileName:{}",fileName);
                    ZipEntry zipEntry = new ZipEntry(fileName);
                    zos.putNextEntry(zipEntry);
                    Files.copy(path, zos);
                    zos.closeEntry();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                }
            });
            zos.finish();
            zos.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return bos.toByteArray();
    }


    @Override
    public  Boolean fileDelete(SignUpFileInfoCURDHeaderDto signUpFileInfoCURDHeaderDto) {


        //附件删除：根据id
        if (ObjectUtil.isNotNull(signUpFileInfoCURDHeaderDto.getId())){
            AtomicBoolean deletedFileIfExists = new AtomicBoolean();
            String id = signUpFileInfoCURDHeaderDto.getId();
            try {
                //1.根据Id查询对应的文件信息
                FileInformation fileInformation = fileInformationRepository.findById(id).orElseGet(FileInformation::new);
                ValidationUtil.isNull( fileInformation.getId(),"FileInformation","id",id);
                //2.根据文件信息中保存文件地址进行删除

                Path filePath = Paths.get(this.uploadDir, fileInformation.getFilePath());
                deletedFileIfExists.set(Files.deleteIfExists(filePath));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //3.删除完成后 删除数据库中的文件信息
            if(!deletedFileIfExists.get()){
                return false;
            }
            fileInformationRepository.deleteById(id);
            return true;
        }
        String linkId= signUpFileInfoCURDHeaderDto.getLinkId();
        String fileAttachmentCode = signUpFileInfoCURDHeaderDto.getFileAttachmentCode();
        // 批量删除:关联表Id
        if (ObjectUtil.isNotNull(linkId) && ObjectUtil.isNull(fileAttachmentCode)){
            fileInformationRepository.findByLinkId(linkId).forEach(this::delete);
        }else if ( ObjectUtil.isNotNull(linkId) && ObjectUtil.isNotNull(fileAttachmentCode)){
            //批量删除:附件类型名称 + 关联表Id
            fileInformationRepository.findByLinkIdAndFileAttachmentCode(linkId, fileAttachmentCode).forEach(this::delete);
        }
        return true;
    }

    private void delete (FileInformation fileInformation) {
        AtomicBoolean deletedFileIfExists = new AtomicBoolean();
        try {
            Path filePath = Paths.get(this.uploadDir, fileInformation.getFilePath());
            deletedFileIfExists.set(Files.deleteIfExists(filePath));
            Path parent = filePath.getParent();
            File file = parent.toFile();
            //如果文件父目录 没有其他文件则进行删除
            if(file.isDirectory() && Objects.requireNonNull(file.listFiles()).length == 0 ){
                file.delete();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if(deletedFileIfExists.get()){
            fileInformationRepository.deleteById(fileInformation.getId());
        }
    }
    @Override
    public void submitCheck(String gcId) {
        List<String> collects = epService.findByTypeLabel("ATTACHMENT").stream().map(EnrollmentParameterDto::getTypeCode).collect(Collectors.toList());
        Set<FileInformation> byGcId = fileInformationRepository.findByGcId(gcId).stream().filter(file -> {
                boolean flag = false;
                for (String str : collects){
                    //如果对比过的就不需要对比了
                    if (tmp.equals(str)){
                        continue;
                    }
                    if (str.equals(file.getFileAttachmentCode())){
                        tmp =str;
                        flag= true;
                        break;
                    }
                }
                return flag;
            }).collect(Collectors.toSet());
        log.info("byGcId:{}",byGcId.size());
        log.info("collects:{}",collects.size());
        if (byGcId.size() != collects.size()){
            throw new BadRequestException("请确保所有必填项填写完成后提交");
        }
    }

}